/*
 *  SnackMidi.cpp
 *  snackbreak
 *
 *  Created by Rua Haszard Morris on 11/21/07.

	MIDI support code, i.e. all the code that interacts with portmidi.

 */

#include "SnackMidi.h"
#include "porttime.h"

// list midi devices with portmidi ids to stdout
void SnackMidi::EnumerateMidiInDevices(std::ostream& strout/* = std::cout*/)
{
    for (int i = 0; i < Pm_CountDevices(); i++) {
        const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
        if (info->input) {
            strout << i << ": " << info->interf << info->name; 
			//printf("%d: %s, %s", i, info->interf, info->name);
            if (info->input) strout << " (input)";//printf(" (input)");
            if (info->output) strout << " (output)";//printf(" ()");
            //printf("\n");
			strout << std::endl;
        }
    }
}

// returns the velocity value if a note on, or 0 if a note off (or a zero-velocity note on!)
int SnackMidi::IsNoteOn(PmMessage msg)
{
	char stat = Pm_MessageStatus(msg);
 	if ( (stat & 0xF0) == 0x90 )
		return Pm_MessageData2(msg);
	
	return 0;
}



SnackMidi::SnackMidi()
: 
	midi(NULL)
{
}

SnackMidi::~SnackMidi()
{
	CloseInput();
}

bool SnackMidi::OpenInput()
{
	return OpenInput(Pm_GetDefaultInputDeviceID());
}

bool SnackMidi::OpenInput(PmDeviceID midiDev)
{
	PmError pmerr = Pm_Initialize( ); // this should be done once, this is a class method, so if I go for 2 midi inputs at some stage this can be moved out to a static method to be called once

	if (pmerr == pmNoError)
	{
		// It is recommended to start timer before Midi; otherwise, PortMidi may
		// start the timer with its (default) parameters
		Pt_Start(1, 0, 0);

		Pm_OpenInput(&midi, 
					 midiDev,
					 NULL, 
					 100, 
					 ((long (*)(void *)) Pt_Time), 
					 NULL);

		if (midi != NULL)
		{
			Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK);
			
			// empty the buffer after setting filter, just in case anything
			// got through 
			PmEvent buffer[1];
			while (Pm_Poll(midi)) 
				Pm_Read(midi, buffer, 1);

			return true;
		}
	}
	
	return false;
}

void SnackMidi::CloseInput()
{
	/*pmerr =*/ Pm_Terminate(); // likewise (should be done once at program termination
}

bool SnackMidi::CheckForEvent(MidiEvent& eventInfo)
{
    PmError status, length;
    PmEvent buffer[1];

	if (midi != NULL)
	{
		status = Pm_Poll(midi);
		if (status == TRUE) {
			length = Pm_Read(midi,buffer, 1);
			
			if (length > 0) {
/*				
				printf("Got message: time %ld, %2lx %2lx %2lx\n",
					   buffer[0].timestamp,
					   Pm_MessageStatus(buffer[0].message),
					   Pm_MessageData1(buffer[0].message),
					   Pm_MessageData2(buffer[0].message));
*/				
				return MidiEvent::MakeMidiEvent(
					Pm_MessageStatus(buffer[0].message),
					Pm_MessageData1(buffer[0].message),
					Pm_MessageData2(buffer[0].message),
					eventInfo
				);
			} else {
				printf("Zero length portmidi buffer!");
			}
		}
	}

	return 0;
}
/*
int SnackMidi::CheckForNoteOn()
{
	MidiEvent dud;
	if (CheckForEvent(dud))
		return dud.isNoteOn();
	else
		return false;
}

double SnackMidi::CheckForPitch()
{
	MidiEvent dud;
	if (CheckForEvent(dud))
		return dud.getPitchBend();
	else
		return 0;
}
*/
